package com.innovate.nms.service.command;

import java.text.MessageFormat;
import java.util.concurrent.TimeoutException;

import com.google.common.eventbus.Subscribe;
import com.innovate.nms.domain.SmsConfig;
import com.innovate.nms.service.channel.Channel;
import com.innovate.nms.service.channel.ChannelEvent;
import com.ivan.toolkit.common.NotifyWaiter;

import lombok.Getter;
import lombok.Setter;



public abstract class Command {
		
	private static int globalIndex = 0;
	
	public synchronized static int nextCommandIndex() {
		if(globalIndex == 1000) 
			globalIndex = 0;
		return globalIndex++;
	}
	
	/**
	 * ָ�����
	 */
	int index = 0;
	
	/**
	 * ������Ϣ
	 */
	@Getter
	private String error = "";
	
	
	/**
	 * ָ����ԵĽڵ�
	 */
	@Setter(value=lombok.AccessLevel.PROTECTED)
	private String remote = null; 
	
	
	private String requestPrefix;
	
	protected String buildCommand(String value) {
		SmsConfig config = SmsConfig.instance.get();
		return MessageFormat.format("{0}{1,number,000}>{2}", config.getLeader(), index, value);
	}	
	
	
	public void start(String channel) throws RuntimeException, TimeoutException{
		
		Channel c = Channel.getChannel(channel);
		if(c == null) {
			throw new RuntimeException("Channel:" + channel + " not found.");
		}
		
		if(!c.isReady()) {
			throw new RuntimeException("Channel:" + channel + " not ready.");
		}
				
		prepare();
		
		index = Command.nextCommandIndex();
		
		try {
			String request = buildRequest();
			requestPrefix = request.substring(0, 5);
			c.send(remote, request);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
		
		Waiter w = new Waiter(channel);
		if(!w.doWait(c.getTimeout())) {
			throw new TimeoutException();
		}
		
	}
	
	protected void prepare(){}
	protected abstract String buildRequest();
	protected abstract boolean checkResponse(String response);
	
	class Waiter {
		
		private String channel;
		
		private NotifyWaiter nw = new NotifyWaiter();
		
		public Waiter(String channel) {
			super();
			this.channel = channel;
		}

		public boolean doWait(long timeout) {
			
			Channel.eventbus.register(this);
			
			nw.doWait(timeout);
			
			
			Channel.eventbus.unregister(this);
			
			return nw.isNotified();
			
		}
		
		@Subscribe
		public void onNotified(ChannelEvent event) {
			if(filter(event)) {
				nw.doNotify();
			}
		}

		protected boolean filter(ChannelEvent event) {
			
			if(!event.getChannel().getKey().equals(channel))
				return false;
			
			// address==null ��ʾ��ַ���ü�飬Ϊ�˼��ݶ���è�������ͷ�����ĸ�ʽ
			if(event.getAddress() != null && !event.getAddress().equals(remote))
				return false;
			
			if(!event.getContent().startsWith(requestPrefix)) 
				return false;
			
			if(!checkResponse(event.getContent())) 
				return false;
			
			return true;
		}
		
	}


	
}
